var fs = require('fs');
var filter = require('../lib/analyser/filter').filter;
var EasyMongo  = require('../common').EasyMongo;

var files = [
    "D:\\201505202.log",
    "D:\\20150611142.log",
    "D:\\2015082019.log",
    "D:\\2015060216.log",
    "C:\\Users\\YANGJO\\Downloads\\20150805104.log",
    "C:\\Users\\YANGJO\\Downloads\\2015111693.log",
    "C:\\Users\\YANGJO\\Downloads\\2015120496.log",
    "C:\\Users\\YANGJO\\Downloads\\2015111480.log",
    "C:\\Users\\YANGJO\\Downloads\\20151202133.log",
    "C:\\Users\\YANGJO\\Downloads\\20151215130.log",
    "C:\\Users\\YANGJO\\Downloads\\20150730104.log",
    "C:\\Users\\YANGJO\\Downloads\\2015073072.log",
    "C:\\Users\\YANGJO\\Downloads\\20150716132.log",
    "C:\\Users\\YANGJO\\Downloads\\20150721136.log",
    "C:\\Users\\YANGJO\\Downloads\\20150730118.log",
    "C:\\Users\\YANGJO\\Downloads\\2015061670.log",
    "C:\\Users\\YANGJO\\Downloads\\20150616136.log",
    "C:\\Users\\YANGJO\\Downloads\\20150805105.log"
];


var crawlerResult = [];
var cursor = 0;
function seriesDownload() {
    if (cursor < files.length) {
        fs.readFile(files[cursor], function (err,data) {
            cursor++;
            data= data.toString();

            var tempData = data.split(/\d\d\d\d\/\d\d\/\d\d\s\d\d:\d\d:\d\d\s-\s/);
            if (tempData.length > 2) {
                var tempTime = data.match(/\d\d\d\d\/\d\d\/\d\d\s\d\d:\d\d:\d\d\s-\s/g);
                //console.log(tempData);
                tempTime.forEach(function (e, i) {
                    Array.prototype.push.call(crawlerResult, e + tempData[i+1]);
                });
            } else {
                crawlerResult.push(data);
            }
            seriesDownload();
        });
    } else {
        callback(crawlerResult);
    }
}

function callback(result) {
    console.log(EasyMongo);
    result.forEach(function (segment) {

        var key =sliceKey(segment);
        var type = fetchType(segment);
        var trace = fetchTrace(segment);

        EasyMongo.add("iview-data-test",{
            exceptionKey : key,
            isRaw : "0",
            times : "1/1",
            status : "define",
            columnName : new Date().valueOf(),
            lastAppear : new Date().valueOf(),
            exceptionType : type,
            trace : trace
        },function(){
        });
    });
}

function sliceKey(data) {
    /**
     * Real exceptionKey is a vector with 4 dimension, unique exception,
     * but for better performance, just save error message & trace segments here.
     * when calculate real key, this attribute should combine with type(attribute), weight(global)
     * */
    var vector = [];
    var start = data.match(/Exception:/).index + 10;
    var end = data.match(/\n.+\s+at\s+/).index;
    vector.push(data.substring(start,end));

    var temp = data.split("\n");
    var traceSegment = "";
    for(var i = 0,j=0;i<temp.length;i++) {
        if(temp[i].match(/\s+at\s+/)) {
            if(++j<=5 || temp[i].match(/\.wos\./)) {
                traceSegment += temp[i].substring(temp[i].indexOf("(")+1, temp[i].lastIndexOf(")"));
            } else {
                break;
            }
        }
    }
    vector.push(traceSegment);
    return vector;
}

function fetchType(data) {
    data = filter(data,"R/\\.(\\w+Exception):/i$1");
    return data;
}

function fetchTrace(data) {
    var temp = filter(data,"R/\\.(\\w+Exception):(.+)/i$2");
    return temp +data.substring(data.indexOf("\n")).trim();
}

var str = "";
/**
 *  test algorithm
 * */
EasyMongo.find('iview-data', {}, function (err, docs) {
    if (err) {
        console.log("IView Target Executor: DB ERROR in checking progress.");
    } else {
        /* Define Weight Table */
        var weightTable = {
            exceptionWeight: {
                "DatabaseException": 0,
                "ExecutorFailureException": 0,
                "NullPointerException": 1,
                "RuntimeException": 1,
                "Default": 2,
                "RemoteException": 3,
                "IllegalArgumentException": 6,
                "NoSuchMethodException": 5,
                "TokenExpirationException": 10,
                "InvalidTokenException": 10,

                "Impact" : 0.92,
                "Scale" : 10
            },
            dimensionWeight: {
                exceptionMsgSimilarity: 1,
                traceSimilarity: 2
            },

            critical_K: 0.95
        };

        var tempSimi={};
        /* Levenshtein Algorithm */
        var Levenshtein = {
            _str1: null,
            _str3: null,
            _matrix: null,
            _isString: function (s) {
                return Object.prototype.toString.call(s) === '[object String]';
            },
            _isNumber: function (s) {
                return Object.prototype.toString.call(s) === '[object Number]';
            },
            init: function (str1, str2) {
                if (!this._isString(str1) || !this._isString(str2)) return;

                this._str1 = str1;
                this._str2 = str2;

                str1.length && str2.length && this._createMatrix(str1.length + 1, str2.length + 1);
                this._matrix && this._initMatrix();

                return this;
            },
            get: function () {
                return 1 - this._getDistance() / Math.max(this._str1.length, this._str2.length);
            },
            _getDistance: function () {
                var len1 = this._str1.length,
                    len2 = this._str2.length;

                if (!len1 || !len2) return Math.max(len1, len2);

                var str1 = this._str1.split(''),
                    str2 = this._str2.split('');

                var i = 0, j = 0, temp = 0;
                while (i++ < len1) {
                    j = 0;
                    while (j++ < len2) {
                        temp = str1[i - 1] === str2[j - 1] ? 0 : 1;
                        this._matrix[i][j] = Math.min(this._matrix[i - 1][j] + 1, this._matrix[i][j - 1] + 1, this._matrix[i - 1][j - 1] + temp);
                    }
                }
                return this._matrix[i - 1][j - 1];
            },
            _initMatrix: function () {
                var cols = this._matrix[0].length,
                    rows = this._matrix.length;
                var l = Math.max(cols, rows);
                while (l--) {
                    cols - 1 >= l && (this._matrix[0][l] = l);
                    rows - 1 >= l && (this._matrix[l][0] = l);
                }
            },
            _createMatrix: function (n, m) {
                if (!this._isNumber(n) || !this._isNumber(m) || n < 1 || m < 1) return;

                this._matrix = new Array(n), i = 0;
                while (i < n) this._matrix[i++] = new Array(m);
            }
        };

        console.log("Target Analyze : Step 1 -> Sort by time ASC");
        //docs.sort(function (a, b) {
        //    return a.createdAt - b.createdAt;
        //});

        console.log("Target Analyze : Step 2 -> Build NoneRaw & Raw Vector");
        var cachedNotRaw = {};
        var cachedRaw = [], cursor = 0;   //must be sorted, so use Array instead of Object

        for (var i = 0; i < docs.length; i++) {
            buildVector(docs[i]);
            if (i%2 == 0) {
                cachedNotRaw[docs[i].exceptionType] = docs[i].exceptionKey;
            } else {
                cachedRaw.push({id: docs[i]._id, vector: docs[i].exceptionKey});
            }
        }

        console.log("Target Analyze : Step 3 -> Recursive Deal Raw Items");
        (function recursiveMerge() {
            if (cursor < cachedRaw.length) {
                var mergeId = null;
                var similarity;
                for (var i in cachedNotRaw) {
                    similarity = calculateSimilarity(cachedNotRaw[i], cachedRaw[cursor].vector);
                }
                cursor+=1;
                recursiveMerge();

            }
        })();

        fs.writeFileSync("test.log",str);

        function merge(targetId, sourceId, callback) {
            console.log("Target Analyze : Step 4.1 -> Merge Same Items");

            /**
             *
             *    compare date, set target weekly times &
             *         adjust times weekly++,total++
             *    set last appear time
             *    if(solved) set status = again
             *    delete source item
             *
             * */

            callback(null);
        }

        function modify(objId, callback) {
            console.log("Target Analyze : Step 4.2 -> Modify Properties");

            /**
             *    compare date, set target weekly times
             *    set raw = '1'
             * */

            callback(null);
        }

        function buildVector(obj) {
            obj.exceptionKey.push(obj.exceptionType,
                (obj.exceptionType in weightTable.exceptionWeight ?
                        weightTable.exceptionWeight[obj.exceptionType] :
                        weightTable.exceptionWeight['Default']
                ));
        }

        function calculateSimilarity(vector1, vector2) {
            if (vector1[2] !== vector2[2]) {
                return 0;
            } else {
                var d1 = Levenshtein.init(vector1[0], vector2[0]).get();
                var d2 = Levenshtein.init(vector1[1], vector2[1]).get();
                var temp = (d1*weightTable.dimensionWeight.exceptionMsgSimilarity + d2*weightTable.dimensionWeight.traceSimilarity)/
                    (weightTable.dimensionWeight.exceptionMsgSimilarity+weightTable.dimensionWeight.traceSimilarity);
                if(temp == 1 || temp < 0.5) return;
                var avg =temp + (1-temp)*(vector1[3]/weightTable.exceptionWeight.Scale * weightTable.exceptionWeight.Impact);
                if((avg+"") in tempSimi) {
                    return;
                } else {
                    tempSimi[avg+""] = true;
                }
                str += "错误信息1 : "+vector1[0]+"\n"+"错误信息2 : "+vector2[0]+"\n错误信息相似度:"+d1+"\n\n错误主要栈轨迹1: "+vector1[1]+"\n\n错误主要栈轨迹2: "+vector2[1]+"\n栈轨迹相似度"+d2+"\n\n加权相似度: "+temp+",调整后最终相似度:"+avg+"\n" +
                    "---------------------------------------------------------------------------------------------------------\n";
                return avg;
            }
        }
    }
});